<?php
/**
 * 框架 - 控制器类
 * 
 * @version $Id: Controller.php 380 2012-08-31 06:02:32Z linsir123 $
 * @package Core
 */
class Controller
{
	/**
	 * [前/后置方法](允许)
	 */
	const FILTER_ALLOW = "FILTER_ALLOW";
	
	/**
	 * [前/后置方法](拒绝)
	 */
	const FILTER_DENY = "FILTER_DENY";
	
	/**
	 * @var 需要执行“前/后置方法”的方法列表“*”表示全部
	 * @example array('index','client','clent2');
	 * @example '*';
	 */
	public $filterActions = '*';
	
	/**
	 * @var [前/后置方法]的方式
	 */
	public $filterType = self::FILTER_ALLOW;
	
	/**
	 * @var 默认的控制器名称
	 */
	public $defaultAction = 'index';
	
	/**
	 * @var [只供子类使用]控制器方法名称
	 */
	protected $_actionName;
	
	/**
	 * @var [只供子类使用]控制器方法
	 */
	protected $_actionMethod;
	
	/**
	 * @var [只供子类使用]控制器类名称
	 */
	protected $_controllerName;
	
	/**
	 * 前置过滤(供子类覆盖)
	 */
	public function beforeAction()
	{
		/// TODO
	}
	
	/**
	 * 后置过滤(供子类覆盖)
	 */
	public function afterAction()
	{
		/// TODO
	}
	
	/**
	 * 由Dispatcher调用执行控制器方法
	 * 
	 * @param string $controllerName 控制器名
	 * @param string $actionName 方法名
	 * @param bool $linkMethod 是否执行(前/后置)方法
	 */
	public function runAction($controllerName, $actionName = null, $linkMethod = true)
	{
		if (empty($actionName)) 
			$actionName = $this->defaultAction;
			
		$this->_controllerName = $controllerName;
		$this->_actionName = $actionName;
		
		///
		$this->_checkAction();
		
		///
		$methods = $this->_getMethods($linkMethod);
		foreach ($methods as $method) 
			call_user_func_array(array($this, $method), array());
	}
	
	/**
	 * 检测控制器是否绑定系统提供的动作处理
	 */
	private function _checkAction()
	{
		$this->_actionMethod = 'action'.ucfirst($this->_actionName);
			
		///
		method_exists($this, $this->_actionMethod) or 
			App::getDispatcher()->goto404("控制器方法({$this->_actionMethod})不存在。");
	}
	
	/**
	 * 验证方法过虑列表
	 * 
	 * @param bool $linkMethod 是否执行(前/后置)方法
	 * @return array
	 */
	private function _getMethods($linkMethod)
	{
		if ($linkMethod) {
			$type = $this->filterType;
			$actions = $this->filterActions;
			$flag = $actions==='*';
			if ( ! $flag && is_array($actions)) 
				$flag = in_array($this->_actionName, $actions);
				
			///
			if (($flag && $type == self::FILTER_ALLOW) 
				|| ( ! $flag && $type == self::FILTER_DENY)) 
				return array(
					'beforeAction', 
					$this->_actionMethod, 
					'afterAction'
				);
		}
		
		return array($this->_actionMethod);
	}
}
